/********************************************************************
 *                                                                  *
 * THIS FILE IS PART OF THE OggWrit SOFTWARE CODEC SOURCE CODE.     *
 * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS     *
 * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
 * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING.       *
 *                                                                  *
 * THE OggWrit SOURCE CODE IS (C) COPYRIGHT 2003-2007               *
 * by the XIPHOPHORUS Company http://www.xiph.org/                  *
 *                                                                  *
 ********************************************************************

 decode.c: Writ stream decoding

 ********************************************************************/

#include <stdlib.h>
#include <string.h>
#include <writ/writ.h>

static void writ_text_read(oggpack_buffer *opb, writ_text *wt) {
  int i;
  wt->length = oggpack_read(opb, 8);
  wt->string = _ogg_realloc(wt->string, 1 + wt->length);
  for (i = 0; i < wt->length; ++i)
    wt->string[i] = (char)oggpack_read(opb, 8);
  wt->string[i] = 0;
}

int writ_decode_init(writ_state *ws) {
  ws->granulepos = -1;
  ws->num_phrases = 0;
  ws->skip_phrases = 0;
  ws->num_headers = 0;
  ws->phrase.text = _ogg_malloc(sizeof(writ_text));
  ws->phrase.text[0].string = 0;
  ws->opb = _ogg_malloc(sizeof(oggpack_buffer));
  ws->wi = _ogg_malloc(sizeof(writ_info));
  ws->wi->version = 0;
  ws->wi->subversion = 0;
  ws->wi->num_languages = 0;
  ws->wi->languages = 0;
  ws->wi->num_windows = 0;
  ws->wi->windows = 0;
  return 0;
}

int writ_decode_clear(writ_state *ws) {
  int i;
  for (i = 0; i < ws->wi->num_languages; ++i) {
    writ_language *wl = &ws->wi->languages[i];
    _ogg_free(wl->language_name.string);
    _ogg_free(wl->language_desc.string);
    _ogg_free(ws->phrase.text[i].string);
  }
  if (!ws->wi->num_languages) _ogg_free(ws->phrase.text[0].string);
  _ogg_free(ws->phrase.text);
  _ogg_free(ws->wi->languages);
  _ogg_free(ws->wi->windows);
  _ogg_free(ws->wi);
  _ogg_free(ws->opb);
  return 0;
}

int writ_decode_header(writ_state *ws, ogg_packet *op) {
  int typeflag, i, bitx, bity;
/*  if (!op) return -1; */
  oggpack_readinit(ws->opb, op->packet, op->bytes);
  typeflag = oggpack_read(ws->opb, 8);
  if (0x80 & typeflag) return 1; /* not header packet */
  if (typeflag != ws->num_headers) return -1; /* packet out of order */
  if ('tirw' != oggpack_read(ws->opb, 32)) return -1; /* not writ packet */
  switch (typeflag)
  {
  case 0x00:
    if (!op->b_o_s) return -1; /* not initial packet */
    ws->wi->version = oggpack_read(ws->opb, 8);
    ws->wi->subversion = oggpack_read(ws->opb, 8);
    ws->wi->granulerate_numerator = oggpack_read(ws->opb, 32);
    ws->wi->granulerate_denominator = oggpack_read(ws->opb, 32);
    break;
  case 0x01:
    ws->wi->num_languages = oggpack_read(ws->opb, 8);
    if (ws->wi->num_languages) {
      ws->wi->languages =
        _ogg_malloc(ws->wi->num_languages * sizeof(writ_language));
      _ogg_free(ws->phrase.text[0].string);
      ws->phrase.text = _ogg_realloc(ws->phrase.text,
        ws->wi->num_languages * sizeof(writ_text));
    }
    for (i = 0; i < ws->wi->num_languages; ++i) {
      writ_language *wl = &ws->wi->languages[i];
      wl->language_name.string = 0;
      wl->language_desc.string = 0;
      writ_text_read(ws->opb, &wl->language_name);
      writ_text_read(ws->opb, &wl->language_desc);
      ws->phrase.text[i].string = 0;
    }
    break;
  case 0x02:
    ws->wi->location_scale_x = (ogg_uint16_t)oggpack_read(ws->opb, 16);
    ws->wi->location_scale_y = (ogg_uint16_t)oggpack_read(ws->opb, 16);
    ws->wi->num_windows = oggpack_read(ws->opb, 8);
    if (ws->wi->num_windows) {
      ws->wi->windows = _ogg_malloc(ws->wi->num_windows * sizeof(writ_window));
      bitx = writ_ilog(ws->wi->location_scale_x);
      bity = writ_ilog(ws->wi->location_scale_y);
    }
    for (i = 0; i < ws->wi->num_windows; ++i) {
      writ_window *wn = &ws->wi->windows[i];
      wn->location_x = (ogg_uint16_t)oggpack_read(ws->opb, bitx);
      wn->location_y = (ogg_uint16_t)oggpack_read(ws->opb, bity);
      wn->location_width = (ogg_uint16_t)oggpack_read(ws->opb, bitx);
      wn->location_height = (ogg_uint16_t)oggpack_read(ws->opb, bity);
      wn->alignment_x = oggpack_read(ws->opb, 2);
      wn->alignment_y = oggpack_read(ws->opb, 2);
    }
    break;
  default:
    /* do nothing, for compatibility with future versions */
    ;
  }
  ++ws->num_headers;
  return 0;
}

/*
  Should split this into writ_decode_packetin and writ_decode_phraseout?
*/
int writ_decode_phrase(writ_state *ws, ogg_packet *op,
                       ogg_int64_t *start, ogg_int32_t *duration,
                       int language_id, char **text, int *window_id) {
  int i;
  if (ws->num_headers <= ws->wi->subversion) return -1; /*  */
  oggpack_readinit(ws->opb, op->packet, op->bytes);
  if (0xFF != oggpack_read(ws->opb, 8)) return -1;
  ws->phrase.start = (unsigned long)oggpack_read(ws->opb, 32);
  ws->phrase.start |= (ogg_int64_t)oggpack_read(ws->opb, 32) << 32;
  ws->phrase.duration = oggpack_read(ws->opb, 32);
  i = 0;
  do {
    writ_text_read(ws->opb, &ws->phrase.text[i]);
  } while (++i < ws->wi->num_languages);
  if (0 < ws->wi->num_windows)
    ws->phrase.window_id = oggpack_read(ws->opb, 8);
  *start = ws->phrase.start;
  *duration = ws->phrase.duration;
  *text = ws->phrase.text[language_id].string;
  *window_id = ws->phrase.window_id;
  return 0;
}
